/*===================================================================*/
/*                                                                   */
/*                       Mapper 45 (Pirates)                         */
/*                                                                   */
/*===================================================================*/

BYTE Map45_Regs[7];
DWORD Map45_P[4], Map45_Prg0, Map45_Prg1, Map45_Prg2, Map45_Prg3;
DWORD Map45_C[8], Map45_Chr0, Map45_Chr1, Map45_Chr2, Map45_Chr3;
DWORD Map45_Chr4, Map45_Chr5, Map45_Chr6, Map45_Chr7;

BYTE Map45_IRQ_Enable;
BYTE Map45_IRQ_Cnt;
BYTE Map45_IRQ_Latch;

/*-------------------------------------------------------------------*/
/*  Initialize Mapper 45                                             */
/*-------------------------------------------------------------------*/
void Map45_Init()
{
	/* Initialize Mapper */
	MapperInit = Map45_Init;

	/* Write to Mapper */
	MapperWrite = Map45_Write;

	/* Write to SRAM */
	MapperSram = Map45_Sram;

	/* Write to APU */
	MapperApu = Map0_Apu;

	/* Read from APU */
	MapperReadApu = Map0_ReadApu;

	/* Callback at VSync */
	MapperVSync = Map0_VSync;

	/* Callback at HSync */
	MapperHSync = Map45_HSync;

	/* Callback at PPU */
	MapperPPU = Map0_PPU;

	/* Callback at Rendering Screen ( 1:BG, 0:Sprite ) */
	MapperRenderScreen = Map0_RenderScreen;

	/* Set SRAM Banks */
	SRAMBANK = SRAM;

	/* Set ROM Banks */
	Map45_Prg0 = 0;
	Map45_Prg1 = 1;
	Map45_Prg2 = NesHeader.byRomSize * 2 - 2;
	Map45_Prg3 = NesHeader.byRomSize * 2 - 1;

	ROMBANK0 = ROMPAGE(Map45_Prg0);
	Map45_P[0] = Map45_Prg0;
	ROMBANK1 = ROMPAGE(Map45_Prg1);
	Map45_P[1] = Map45_Prg1;
	ROMBANK2 = ROMPAGE(Map45_Prg2);
	Map45_P[2] = Map45_Prg2;
	ROMBANK3 = ROMPAGE(Map45_Prg3);
	Map45_P[3] = Map45_Prg3;

	/* Set PPU Banks */
	Map45_Chr0 = 0;
	Map45_C[0] = Map45_Chr0;
	Map45_Chr1 = 1;
	Map45_C[1] = Map45_Chr1;
	Map45_Chr2 = 2;
	Map45_C[2] = Map45_Chr2;
	Map45_Chr3 = 3;
	Map45_C[3] = Map45_Chr3;
	Map45_Chr4 = 4;
	Map45_C[4] = Map45_Chr4;
	Map45_Chr5 = 5;
	Map45_C[5] = Map45_Chr5;
	Map45_Chr6 = 6;
	Map45_C[6] = Map45_Chr6;
	Map45_Chr7 = 7;
	Map45_C[7] = Map45_Chr7;

	for (int nPage = 0; nPage < 8; ++nPage)
	{
		PPUBANK[nPage] = VROMPAGE(nPage);
	}
	InfoNES_SetupChr();

	/* Initialize IRQ Registers */
	Map45_IRQ_Enable = 0;
	Map45_IRQ_Cnt = 0;
	Map45_IRQ_Latch = 0;

	Map45_Regs[0] = Map45_Regs[1] = Map45_Regs[2] = Map45_Regs[3] = 0;
	Map45_Regs[4] = Map45_Regs[5] = Map45_Regs[6] = 0;

	/* Set up wiring of the interrupt pin */
	K6502_Set_Int_Wiring(1, 1);
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Write to Sram Function                                 */
/*-------------------------------------------------------------------*/
void Map45_Sram(WORD wAddr, BYTE byData)
{
	if (wAddr == 0x6000)
	{
		Map45_Regs[Map45_Regs[5]] = byData;
		Map45_Regs[5] = (Map45_Regs[5] + 1) & 0x03;
		Map45_Set_CPU_Bank4((BYTE)Map45_Prg0);
		Map45_Set_CPU_Bank5((BYTE)Map45_Prg1);
		Map45_Set_CPU_Bank6((BYTE)Map45_Prg2);
		Map45_Set_CPU_Bank7((BYTE)Map45_Prg3);
		Map45_Set_PPU_Banks();
	}
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Write Function                                         */
/*-------------------------------------------------------------------*/
void Map45_Write(WORD wAddr, BYTE byData)
{
	DWORD swap;

	switch (wAddr & 0xE001)
	{
	case 0x8000:
		if ((byData & 0x40) != (Map45_Regs[6] & 0x40))
		{
			swap = Map45_Prg0;
			Map45_Prg0 = Map45_Prg2;
			Map45_Prg2 = swap;
			swap = Map45_P[0];
			Map45_P[0] = Map45_P[2];
			Map45_P[2] = swap;
			ROMBANK0 = ROMPAGE(Map45_P[0] % (NesHeader.byRomSize << 1));
			ROMBANK2 = ROMPAGE(Map45_P[2] % (NesHeader.byRomSize << 1));
		}
		if (NesHeader.byRomSize > 0)
		{
			if ((byData & 0x80) != (Map45_Regs[6] & 0x80))
			{
				swap = Map45_Chr4;
				Map45_Chr4 = Map45_Chr0;
				Map45_Chr0 = swap;
				swap = Map45_Chr5;
				Map45_Chr5 = Map45_Chr1;
				Map45_Chr1 = swap;
				swap = Map45_Chr6;
				Map45_Chr6 = Map45_Chr2;
				Map45_Chr2 = swap;
				swap = Map45_Chr7;
				Map45_Chr7 = Map45_Chr3;
				Map45_Chr3 = swap;
				swap = Map45_C[4];
				Map45_C[4] = Map45_C[0];
				Map45_C[0] = swap;
				swap = Map45_C[5];
				Map45_C[5] = Map45_C[1];
				Map45_C[1] = swap;
				swap = Map45_C[6];
				Map45_C[6] = Map45_C[2];
				Map45_C[2] = swap;
				swap = Map45_C[7];
				Map45_C[7] = Map45_C[3];
				Map45_C[3] = swap;

				PPUBANK[0] = VROMPAGE(Map45_C[0] % (NesHeader.byVRomSize << 3));
				PPUBANK[1] = VROMPAGE(Map45_C[1] % (NesHeader.byVRomSize << 3));
				PPUBANK[2] = VROMPAGE(Map45_C[2] % (NesHeader.byVRomSize << 3));
				PPUBANK[3] = VROMPAGE(Map45_C[3] % (NesHeader.byVRomSize << 3));
				PPUBANK[4] = VROMPAGE(Map45_C[4] % (NesHeader.byVRomSize << 3));
				PPUBANK[5] = VROMPAGE(Map45_C[5] % (NesHeader.byVRomSize << 3));
				PPUBANK[6] = VROMPAGE(Map45_C[6] % (NesHeader.byVRomSize << 3));
				PPUBANK[7] = VROMPAGE(Map45_C[7] % (NesHeader.byVRomSize << 3));
				InfoNES_SetupChr();
			}
		}
		Map45_Regs[6] = byData;
		break;

	case 0x8001:
		switch (Map45_Regs[6] & 0x07)
		{
		case 0x00:
			Map45_Chr0 = (byData & 0xFE) + 0;
			Map45_Chr1 = (byData & 0xFE) + 1;
			Map45_Set_PPU_Banks();
			break;

		case 0x01:
			Map45_Chr2 = (byData & 0xFE) + 0;
			Map45_Chr3 = (byData & 0xFE) + 1;
			Map45_Set_PPU_Banks();
			break;

		case 0x02:
			Map45_Chr4 = byData;
			Map45_Set_PPU_Banks();
			break;

		case 0x03:
			Map45_Chr5 = byData;
			Map45_Set_PPU_Banks();
			break;

		case 0x04:
			Map45_Chr6 = byData;
			Map45_Set_PPU_Banks();
			break;

		case 0x05:
			Map45_Chr7 = byData;
			Map45_Set_PPU_Banks();
			break;

		case 0x06:
			if (Map45_Regs[6] & 0x40)
			{
				Map45_Prg2 = byData & 0x3F;
				Map45_Set_CPU_Bank6(byData);
			}
			else
			{
				Map45_Prg0 = byData & 0x3F;
				Map45_Set_CPU_Bank4(byData);
			}
			break;

		case 0x07:
			Map45_Prg1 = byData & 0x3F;
			Map45_Set_CPU_Bank5(byData);
			break;
		}
		break;

	case 0xA000:
		if (byData & 0x01)
		{
			InfoNES_Mirroring(0);
		}
		else
		{
			InfoNES_Mirroring(1);
		}
		break;

	case 0xC000:
		Map45_IRQ_Cnt = byData;
		break;

	case 0xC001:
		Map45_IRQ_Latch = byData;
		break;

	case 0xE000:
		Map45_IRQ_Enable = 0;
		break;

	case 0xE001:
		Map45_IRQ_Enable = 1;
		break;
	}
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 H-Sync Function                                        */
/*-------------------------------------------------------------------*/
void Map45_HSync()
{
	/*
 *  Callback at HSync
 *
 */
	if (Map45_IRQ_Enable)
	{
		if (0 <= PPU_Scanline && PPU_Scanline <= 239)
		{
			if (PPU_R1 & R1_SHOW_SCR || PPU_R1 & R1_SHOW_SP)
			{
				if (!(Map45_IRQ_Cnt--))
				{
					Map45_IRQ_Cnt = Map45_IRQ_Latch;
					IRQ_REQ;
				}
			}
		}
	}
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Set CPU Banks Function                                 */
/*-------------------------------------------------------------------*/

void Map45_Set_CPU_Bank4(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
	ROMBANK0 = ROMPAGE(byData % (NesHeader.byRomSize << 1));
	Map45_P[0] = byData;
}

void Map45_Set_CPU_Bank5(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
	ROMBANK1 = ROMPAGE(byData % (NesHeader.byRomSize << 1));
	Map45_P[1] = byData;
}

void Map45_Set_CPU_Bank6(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
	ROMBANK2 = ROMPAGE(byData % (NesHeader.byRomSize << 1));
	Map45_P[2] = byData;
}

void Map45_Set_CPU_Bank7(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
	ROMBANK3 = ROMPAGE(byData % (NesHeader.byRomSize << 1));
	Map45_P[3] = byData;
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Set PPU Banks Function                                 */
/*-------------------------------------------------------------------*/
void Map45_Set_PPU_Banks()
{
	BYTE table[16] =
		{
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
	Map45_C[0] = Map45_Chr0;
	Map45_C[1] = Map45_Chr1;
	Map45_C[2] = Map45_Chr2;
	Map45_C[3] = Map45_Chr3;
	Map45_C[4] = Map45_Chr4;
	Map45_C[5] = Map45_Chr5;
	Map45_C[6] = Map45_Chr6;
	Map45_C[7] = Map45_Chr7;
	for (BYTE i = 0; i < 8; i++)
	{
		Map45_C[i] &= table[Map45_Regs[2] & 0x0F];
		Map45_C[i] |= Map45_Regs[0] & 0xff;
		Map45_C[i] += (BYTE)(Map45_Regs[2] & 0x10) << 4;
	}
	if (Map45_Regs[6] & 0x80)
	{
		PPUBANK[0] = VROMPAGE(Map45_C[4] % (NesHeader.byVRomSize << 3));
		PPUBANK[1] = VROMPAGE(Map45_C[5] % (NesHeader.byVRomSize << 3));
		PPUBANK[2] = VROMPAGE(Map45_C[6] % (NesHeader.byVRomSize << 3));
		PPUBANK[3] = VROMPAGE(Map45_C[7] % (NesHeader.byVRomSize << 3));
		PPUBANK[4] = VROMPAGE(Map45_C[0] % (NesHeader.byVRomSize << 3));
		PPUBANK[5] = VROMPAGE(Map45_C[1] % (NesHeader.byVRomSize << 3));
		PPUBANK[6] = VROMPAGE(Map45_C[2] % (NesHeader.byVRomSize << 3));
		PPUBANK[7] = VROMPAGE(Map45_C[3] % (NesHeader.byVRomSize << 3));
		InfoNES_SetupChr();
	}
	else
	{
		PPUBANK[0] = VROMPAGE(Map45_C[0] % (NesHeader.byVRomSize << 3));
		PPUBANK[1] = VROMPAGE(Map45_C[1] % (NesHeader.byVRomSize << 3));
		PPUBANK[2] = VROMPAGE(Map45_C[2] % (NesHeader.byVRomSize << 3));
		PPUBANK[3] = VROMPAGE(Map45_C[3] % (NesHeader.byVRomSize << 3));
		PPUBANK[4] = VROMPAGE(Map45_C[4] % (NesHeader.byVRomSize << 3));
		PPUBANK[5] = VROMPAGE(Map45_C[5] % (NesHeader.byVRomSize << 3));
		PPUBANK[6] = VROMPAGE(Map45_C[6] % (NesHeader.byVRomSize << 3));
		PPUBANK[7] = VROMPAGE(Map45_C[7] % (NesHeader.byVRomSize << 3));
		InfoNES_SetupChr();
	}
}
